﻿using System;
using Oxage.CNC.Models;

namespace Oxage.Simulator
{
	[InlineBinding(typeof(ILineInline))]
	public class LineStepMaker : IStepMaker
	{
		#region Public methods
		/// <summary>
		/// Mathematical solution to draw a line on a plane (2 dimensions)
		/// </summary>
		public void Line(IStepDriver driver, int x0, int y0, int x1, int y1)
		{
			int dy = y1 - y0;
			int dx = x1 - x0;
			int stepX, stepY;
			int dyReal, dxReal;
			int fraction;
			int xStep = 0;
			int yStep = 0;

			dyReal = dy;
			dxReal = dx;

			if (dy < 0) { dy = -dy; stepY = -1; } else { stepY = 1; }
			if (dx < 0) { dx = -dx; stepX = -1; } else { stepX = 1; }

			dy <<= 1; //2 * dy
			dx <<= 1; //2 * dx

			//MoveAbs(x0, y0, null)
			//sim.world.z = x0;
			//sim.world.y = y0;
			//sim.DrawTool();

			if (dx > dy)
			{
				fraction = dy - (dx >> 1); //2 * dy - dx
				while (x0 != x1)
				{
					if (fraction >= 0)
					{
						y0 += stepY;
						fraction -= dx; //fraction -= 2 * dx
					}
					x0 += stepX;
					fraction += dy; //fraction -= 2 * dy

					if (dxReal > 0)
					{
						xStep = 1;
					}

					if (dyReal > 0)
					{
						yStep = 1;
					}

					if (dxReal < 0)
					{
						xStep = -1;
					}

					if (dyReal < 0)
					{
						yStep = -1;
					}

					//driver.Step(xStep, yStep, 0);
					driver.Step(new NPoint { X = xStep, Y = yStep });
					xStep = 0;
					yStep = 0;
				}
			}
			else
			{
				fraction = dx - (dy >> 1);
				while (y0 != y1)
				{
					if (fraction >= 0)
					{
						x0 += stepX;
						fraction -= dy;
					}
					y0 += stepY;
					fraction += dx;

					if (dxReal > 0)
					{
						xStep = 1;
					}

					if (dyReal > 0)
					{
						yStep = 1;
					}

					if (dxReal < 0)
					{
						xStep = -1;
					}

					if (dyReal < 0)
					{
						yStep = -1;
					}

					//driver.Step(xStep, yStep, 0);
					driver.Step(new NPoint { X = xStep, Y = yStep });
					xStep = 0;
					yStep = 0;
				}
			}
		}
		#endregion

		#region IStepMaker Members
		public void Run(IStepDriver driver, object data)
		{
			if (driver == null)
			{
				throw new ArgumentNullException("driver");
			}

			var inline = data as ILineInline;
			if (inline == null)
			{
				throw new ArgumentException("Expected ILineInline");
			}

			Line(driver, (int)driver.Cursor.X, (int)driver.Cursor.Y, (int)inline.EndPoint.X, (int)inline.EndPoint.Y);
		}
		#endregion
	}
}
